const axios = require('axios')
const pathUtils = require('path')
const fs = require('fs')
const commander = require('commander')

// 定义选项
commander
  .option('-s --secretKey <type>', 'apifox个人密钥')
  .option('-p --projectId <type>', 'apifox项目id')
  .option('-f --filter <string>', '匹配该tags下的所有api', '')
  .parse(process.argv)

const { secretKey, projectId, filter } = commander

// 手动验证必填参数
if (!secretKey) {
  console.error('\x1b[31m%s\x1b[0m', 'Error: 缺少apifox个人密钥,请检查')
  process.exit(1) // 退出进程并返回错误码
}

if (!projectId) {
  console.error('\x1b[31m%s\x1b[0m', 'Error: 缺少pifox项目id,请检查')
  process.exit(1)
}

// 手动验证必填参数
if (!filter) {
  console.error('\x1b[31m%s\x1b[0m', 'Error: 缺少匹配tags参数,请检查')
  process.exit(1) // 退出进程并返回错误码
}

const npmScriptName = process.env.npm_lifecycle_event

// api:xx 当前xx值 服务名
let serviceName = ''

if (npmScriptName && npmScriptName.startsWith('api:')) {
  // 提取冒号后面的部分
  serviceName = npmScriptName.split(':')[1]
}

// 数据类型
let currentType = ''

// body是否必填
let isBodyRequired = false

// params是否必填
let isParamsRequired = false

// 生成接口数量
let apiCount = 0

// 接口文档JSON
let swagger = {}

// 生成ts逻辑
function generateTypeScriptDefinitions (Val) {
  let childBodyTsDefinitions = ''
  let childQueryTsDefinitions = ''
  let childDataTsDefinitions = ''
  // 是否有query参数
  const isQuery = containsKeyValue(Val.parameters, 'in', 'query')
  // 是否有fromData参数
  const isFormData = containsKeyValue(Val.parameters, 'in', 'formData')
  // 是否有body参数
  const isBody = containsKeyValue(Val.parameters, 'in', 'body') || isFormData
  // 初始化
  isBodyRequired = false
  isParamsRequired = false
  // body参数逻辑
  if (isBody) {
    let data = getObjectsWithKeyValue(Val.parameters, 'in', 'body')[0]

    if (!isFormData) {
      if (data.schema.$ref) {
        const parts = data.schema.$ref.split('/') // 将字符串按 '/' 分割
        const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
        data = swagger.definitions[lastWord].properties
      } else {
        data = data.schema.properties ? data.schema.properties : {}
      }
      childBodyTsDefinitions += 'interface RequestData {\n'
      childBodyTsDefinitions += getBodyTs(data)
      childBodyTsDefinitions += '}\n\n'
    } else {
      data = getObjectsWithKeyValue(Val.parameters, 'in', 'formData')[0]
      isBodyRequired = true
      childBodyTsDefinitions += 'interface RequestData {\n'
      childBodyTsDefinitions += '  /** 文件 */\n'
      childBodyTsDefinitions += `  files: ${javaToTypeScriptType('string')}\n`
      childBodyTsDefinitions += '}\n\n'
    }
  }

  // query参数逻辑
  if (isQuery) {
    const data = getObjectsWithKeyValue(Val.parameters, 'in', 'query')
    if (data.length) {
      childQueryTsDefinitions += 'interface RequestParams {\n'
      for (const [key, value] of Object.entries(data)) {
        if (value.required) {
          isParamsRequired = true
        }
        childQueryTsDefinitions += `  /** ${value.title ? value.title : value.description ? value.description : ''} */\n`
        childQueryTsDefinitions += `  ${value.name}${isParamsRequired ? '' : '?'}: ${javaToTypeScriptType(value.type)}\n`
      }
      childQueryTsDefinitions += '}\n\n'
    }
  }

  // response参数逻辑
  let respData = Val.responses['200']
  if (respData.schema.type === 'boolean') {
    currentType = 'boolean'
    return childBodyTsDefinitions + childQueryTsDefinitions
  }
  if (respData.schema.type === 'object' && typeof respData.schema.properties === 'object' && respData.schema.properties !== null && Object.keys(respData.schema.properties).length === 0) {
    currentType = '{}'
    return childBodyTsDefinitions + childQueryTsDefinitions
  }
  if (respData.schema.$ref) {
    const parts = respData.schema.$ref.split('/') // 将字符串按 '/' 分割
    const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
    respData = swagger.definitions[lastWord]
  } else {
    respData = respData.schema
  }
  childDataTsDefinitions += getReponseTs(respData)
  const childTsDefinitions = childBodyTsDefinitions + childQueryTsDefinitions + childDataTsDefinitions
  return childTsDefinitions
}

// body递归
function getBodyTs (Val, indentLevel = 1) {
  let tsItems = ''
  const indent = '  '.repeat(indentLevel) // 每一级缩进两个空格
  for (const [key, value] of Object.entries(Val)) {
    if (value && value.$ref) {
      const parts = value.$ref.split('/') // 将字符串按 '/' 分割
      const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
      const refVal = swagger.definitions[lastWord].properties
      tsItems += `${indent}${key}?: {\n`
      tsItems += getBodyTs(refVal, indentLevel + 1)
      tsItems += `${indent}}\n`
    } else {
      if (value['x-nullable'] || !value.required) {
        isBodyRequired = false
      }
      tsItems += `${indent}/** ${value.title ? value.title : value.description ? value.description : ''} */\n`
      tsItems += `${indent}${key}${isBodyRequired ? '' : '?'}: ${javaToTypeScriptType(value.type) + (value['x-nullable'] ? ' | null' : '')}\n`
    }
  }
  return tsItems
}

// response获取
function getReponseTs (Val) {
  let tsItems = ''
  let newVal = ''
  // 返回格式1：当前properties存在code以及data 则直接去data的值做类型
  if ((Val.properties && Val.properties.data && Val.properties.code) || (Val.properties && Val.properties.data && Val.properties.err)) {
    newVal = Val.properties.data
    if (newVal.type === 'string' || newVal.type === 'boolean') {
      currentType = newVal.type
      return ''
    }

    if (newVal.type === 'integer' || newVal.type === 'number') {
      currentType = 'number'
      return ''
    }

    if (newVal.type === 'object' && typeof newVal.properties === 'object' && newVal.properties !== null && Object.keys(newVal.properties).length === 0) {
      currentType = '{}'
      return ''
    }

    if (newVal.type === 'null' || (typeof newVal === 'object' && newVal !== null && Object.keys(newVal).length === 0)) {
      currentType = 'null'
      return ''
    }

    if (newVal.type === 'object') {
      tsItems += 'interface Data {\n'
      if (newVal.$ref) {
        const parts = newVal.$ref.split('/') // 将字符串按 '/' 分割
        const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
        const refVal = swagger.definitions[lastWord].properties
        tsItems += getMoreTs(refVal)
      } else {
        tsItems += getMoreTs(newVal.properties)
      }
      tsItems += '}\n\n'
      currentType = 'Data'
      return tsItems
    }
    if (newVal.type === 'array') {
      tsItems += 'interface Data {\n'
      if (newVal.items.$ref) {
        const parts = newVal.items.$ref.split('/') // 将字符串按 '/' 分割
        const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
        const refVal = swagger.definitions[lastWord].properties
        tsItems += getMoreTs(refVal)
      } else if (newVal.items.properties) {
        tsItems += getMoreTs(newVal.items.properties)
      }
      tsItems += '}\n\n'
      currentType = 'Data[]'
      return tsItems
    }

    const parts = newVal.$ref.split('/') // 将字符串按 '/' 分割
    const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
    const refVal = swagger.definitions[lastWord].properties
    tsItems += 'interface Data {\n'
    tsItems += getMoreTs(refVal)
    tsItems += '}\n\n'
    currentType = 'Data'
    return tsItems
  } else {
    if (Val.type === 'object') {
      tsItems += 'interface Data {\n'
      if (Val.$ref) {
        const parts = Val.$ref.split('/') // 将字符串按 '/' 分割
        const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
        const refVal = swagger.definitions[lastWord].properties
        tsItems += getMoreTs(refVal)
      } else {
        tsItems += getMoreTs(Val.properties)
      }
      tsItems += '}\n\n'
      currentType = 'Data'
      return tsItems
    }

    if (Val.type === 'array') {
      tsItems += 'interface Data {\n'
      if (Val.items.$ref) {
        const parts = Val.items.$ref.split('/') // 将字符串按 '/' 分割
        const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
        const refVal = swagger.definitions[lastWord].properties
        tsItems += getMoreTs(refVal)
      } else {
        tsItems += getMoreTs(Val.items.properties)
      }
      tsItems += '}\n\n'
      currentType = 'Data[]'
      return tsItems
    }
  }
}

// response递归
function getMoreTs (Val, indentLevel = 1) {
  let tsItems = ''
  const indent = '  '.repeat(indentLevel) // 每一级缩进两个空格
  if (!Val) {
    return tsItems
  }

  for (const [key, value] of Object.entries(Val)) {
    if (value.$ref) {
      const parts = value.$ref.split('/') // 将字符串按 '/' 分割
      const lastWord = decodeURIComponent(parts[parts.length - 1]) // 获取最后一个部分
      const refVal = swagger.definitions[lastWord].properties
      tsItems += `${indent}${key}?: {\n`
      tsItems += getMoreTs(refVal, indentLevel + 1)
      tsItems += `${indent}}\n`
    } else {
      tsItems += ` ${indent}/** ${value.title ? value.title : value.description ? value.description : ''} */\n`
      tsItems += ` ${indent}${key}?: ${javaToTypeScriptType(value.type) + (value['x-nullable'] ? ' | null' : '')}\n `
    }
  }
  return tsItems
}

let currentFunctionName = ''

// 生成api.
function generateApiFunctions (paths) {
  // 引入api项
  let apiImportItems = ''
  for (const [path, methods] of Object.entries(paths)) {
    // api格式化语句
    let apiFunctions = ''
    // 导入语句
    let importItems = ''
    // ts格式化语句
    let tsDefinitions = ''
    importItems += 'import { ResponseData } from \'@one/core\'\n'
    const axiosImprot = serviceName ? 'import axios$ from \'../axios.ts\'\n\n' : 'import axios$ from \'./axios.ts\'\n\n'
    importItems += axiosImprot
    for (const [method, details] of Object.entries(methods)) {
      tsDefinitions += generateTypeScriptDefinitions(details)
      // tsDefinitions += tsBlock
      // 是否有path参数
      const isPath = containsKeyValue(details.parameters, 'in', 'path')
      // 是否有query参数
      const isQuery = containsKeyValue(details.parameters, 'in', 'query')
      // 是否有body参数
      const isBody = containsKeyValue(details.parameters, 'in', 'body') || containsKeyValue(details.parameters, 'in', 'formData')
      // 参数类型声明
      let parameterTye = ''
      // 当前接口目录
      const tags = details.tags
      // f参数转数组
      const keywords = filter.split(',')
      // 匹配逻辑
      const isTags = keywords.some(keyword =>
        tags.some(item =>
          item === keyword || item.startsWith(`${keyword}/`)
        )
      )
      if (isTags) {
        apiCount++
        if (method.toLowerCase() === 'get') {
          parameterTye = (isQuery ? `  RequestParams${isParamsRequired ? '' : '?'}: RequestParams` : '  RequestParams?: null')
        } else {
          parameterTye = (isBody ? `  RequestData${isBodyRequired ? '' : '?'}: RequestData` : `  RequestData${isBodyRequired ? '' : '?'}: null`) + ',\n' + (isQuery ? `  RequestParams${isParamsRequired ? '' : '?'}: RequestParams` : '  RequestParams?: null')
        }
        // api规则
        const functionName = `${capitalizeWords(path, 1)}`
        currentFunctionName = functionName
        console.log(`⏳ 接口：${functionName} 正在生成....`)
        apiFunctions += `/**  ${details.summary} */\n`
        apiFunctions += `const ${functionName} = <T = ResponseData<${currentType}>>(\n`
        apiFunctions += `${parameterTye}\n`
        apiFunctions += '): Promise<T> => {\n'
        apiFunctions += '  return axios$({\n'
        apiFunctions += `    url: '${path}',\n`
        apiFunctions += `    method: '${method.toLowerCase()}',\n`
        if (method.toLowerCase() !== 'get') {
          apiFunctions += '    params: RequestParams,\n'
          apiFunctions += '    data: RequestData\n'
        } else {
          apiFunctions += '    params: RequestParams\n'
        }
        apiFunctions += '  })\n'
        apiFunctions += '}\n\n'
        apiFunctions += `export default ${functionName}\n`
        // 写入逻辑
        const fileContent = importItems + tsDefinitions + apiFunctions
        apiImportItems += `export { default as ${functionName} } from './${functionName}.ts'\n`
        const filePath = `./src/api${serviceName ? '/' + serviceName : ''}/${functionName}.ts`
        // 提取目录路径
        const dir = pathUtils.dirname(filePath)
        if (fs.existsSync(filePath)) {
          fs.rmSync(filePath)
        }
        fs.mkdir(dir, { recursive: true }, (err) => {
          if (err) {
            return console.error('创建目录失败:', err)
          }
          fs.writeFileSync(filePath, fileContent)
        })
      }
    }
  }
  // 存在serviceName写入规则
  if (fs.existsSync('./src/api/index.ts')) {
    const content = fs.readFileSync('./src/api/index.ts', 'utf8')
    if (serviceName) {
      // 写入服务index.ts文件
      fs.writeFileSync(`./src/api/${serviceName}/index.ts`, apiImportItems)
      const exportItem = `export * from './${serviceName}/index.ts'\n`
      // 检查是否已经存在该导出语句
      if (!content.includes(exportItem)) {
        // 如果不存在，写入
        fs.appendFileSync('./src/api/index.ts', exportItem, 'utf8')
      }
    } else {
      // 保留 `export *` 的语句
      const exportStarLines = content.match(/export\s+\*\s+from\s+['"].+['"]/g) || []
      // 在处理过程中确保每个 export * 语句都在新的一行上
      const formattedExportStarLines = exportStarLines.map(line => line.trim()).join('\n')
      fs.writeFileSync('./src/api/index.ts', apiImportItems + '\n' + formattedExportStarLines + '\n')
    }
  } else {
    if (serviceName) {
      // 写入服务index.ts文件
      fs.writeFileSync(`./src/api/${serviceName}/index.ts`, apiImportItems)
      fs.writeFileSync('./src/api/index.ts', `export * from './${serviceName}/index.ts'\n`)
    } else {
      // 不存在index.ts 且命令为api
      fs.writeFileSync('./src/api/index.ts', apiImportItems)
    }
  }
  console.log('\x1b[32m%s\x1b[0m', `✅ 本次生成接口数：${apiCount}`)
  if (apiCount === 0) {
    console.log('\x1b[33m%s\x1b[0m', '⚠️ ⚠️ ⚠️  (╯‵□′)╯︵┻━┻ 搞咩！？ 一个接口都莫得, 看看参数呀  ⚠️ ⚠️ ⚠️')
    return
  }
  console.log('\x1b[32m%s\x1b[0m', '🎉🎉🎉  API生成完毕，可在目录./src/api找到生成的文件  🎉🎉🎉')
}

// 获取接口文档并生成文件
function getOpenAiData () {
  axios({
    url: `https://api.apifox.com/v1/projects/${projectId}/export-openapi`,
    data: {
      scope: {
        type: 'ALL',
        excludedByTags: ['pet']
      },
      options: {
        includeApifoxExtensionProperties: false,
        addFoldersToTags: true
      },
      oasVersion: '2.0',
      exportFormat: 'JSON'
    },
    method: 'POST',
    headers: {
      'X-Apifox-Api-Version': '2024-03-28',
      Authorization: `Bearer ${secretKey}`
    }
  }).then(res => {
    console.log('\x1b[32m%s\x1b[0m', ' 🚀 🚀 🚀  正在火速生成API...  🚀 🚀 🚀')
    const data = res.data
    swagger = data
    generateApiFunctions(data.paths)
  }).catch(e => {
    console.error('\x1b[31m%s\x1b[0m', `❌ API: <${currentFunctionName}> 生成失败。请检查您的配置并重试。`)
    logError(`Error in getOpenAiData: ${e}\n${e.stack}`)
  })
}

// 命名格式话
function capitalizeWords (str, number) {
  // 格式化只保留英文数字部分
  const newStr = str.replace(/[^a-zA-Z0-9/]/g, '')
  return newStr.split('/').map((word, index) => {
    const Val = index === number ? word : word.charAt(0).toUpperCase() + word.slice(1)
    return Val
  }).join('')
}

// 判断参数是否包含某值
function containsKeyValue (arr, key, value) {
  return arr.some(obj => obj[key] === value)
}

// 获取参数项
function getObjectsWithKeyValue (arr, key, value) {
  return arr.filter(obj => obj[key] === value)
}

// java类型转换为ts类型
function javaToTypeScriptType (javaType) {
  const typeMapping = {
    // 基本类型
    int: 'number',
    long: 'number', // 或者 'bigint'，根据需求
    float: 'number',
    double: 'number',
    boolean: 'boolean',
    char: 'string',
    byte: 'number',
    short: 'number',

    // 引用类型
    String: 'string',
    string: 'string',
    Integer: 'number',
    integer: 'number',
    Long: 'number', // 或者 'bigint'
    Float: 'number',
    Double: 'number',
    Boolean: 'boolean',
    Character: 'string',
    Object: 'any',

    // 集合类型
    List: 'Array', // 需要处理泛型
    Map: 'Map', // 需要处理泛型
    Set: 'Set', // 需要处理泛型

    // 特殊类型
    void: 'void',
    null: 'null',
    undefined: 'null' // 或者 'undefined'，根据上下文
  }

  // 检查是否有直接的映射
  if (typeMapping[javaType]) {
    return typeMapping[javaType]
  }

  // 如果是泛型类型，需要进一步处理
  if (javaType.includes('<') && javaType.includes('>')) {
    const baseType = javaType.substring(0, javaType.indexOf('<'))
    const genericTypes = javaType.substring(javaType.indexOf('<') + 1, javaType.lastIndexOf('>'))

    if (typeMapping[baseType]) {
      const tsBaseType = typeMapping[baseType]
      const tsGenericTypes = genericTypes.split(',').map(t => javaToTypeScriptType(t.trim())).join(', ')

      return `${tsBaseType}<${tsGenericTypes}>`
    }
  }
  return 'any'
}

// 定义日志文件路径
const logFilePath = pathUtils.join(__dirname, 'error.log')

// 写入日志文件的函数
function logError (message) {
  const timestamp = new Date().toISOString()
  const logMessage = `${timestamp} [ERROR]: ${message}`
  fs.appendFileSync(logFilePath, logMessage, 'utf8')
}

getOpenAiData()
